﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

using BrightSword.SwissKnife;

namespace BrightSword.Feber.Core
{
    /// <summary>
    /// Abstract base class from which all builders derive. This class contains the reflective operation on the <typeparamref name="TProto"/> type.
    /// </summary>
    /// <typeparam name="TProto">The type whose properties are to be reflected over for further operation.</typeparam>
    public abstract class OperationBuilderBase<TProto>
    {
        /// <summary>
        /// Override this property to provide a filter for the set of properties of <typeparamref name="TProto"/> included for processing.
        /// </summary>
        protected virtual Func<PropertyInfo, bool> PropertyFilter
        {
            get { return _ => true; }
        }

        /// <summary>
        /// Readonly list of properties satisfying the <see cref="PropertyFilter"/> predicate specified
        /// </summary>
        public virtual IEnumerable<PropertyInfo> FilteredProperties
        {
            get
            {
                return from _propertyInfo in typeof (TProto).GetAllProperties()
                       where PropertyFilter(_propertyInfo)
                       select _propertyInfo;
            }
        }

        /// <summary>
        /// This property will return the result of applying the <see cref="BuildPropertyExpression"/> map function on each of the <see cref="FilteredProperties"/>. 
        /// Typically, this does not need to be overriden.
        /// </summary>
        protected virtual IEnumerable<Expression> OperationExpressions
        {
            get { return FilteredProperties.Select(BuildPropertyExpression); }
        }

        /// <summary>
        /// Provide an implementation of this method to specify the action to be taken on a property to convert it to a composable expression.
        /// The <see cref="OperationExpressions"/> property uses this as a mapping function to apply to a set of filtered properties in turn.
        /// </summary>
        /// <param name="propertyInfo">A given property to map into an Expression</param>
        /// <returns>The mapped Expression</returns>
        protected abstract Expression BuildPropertyExpression(PropertyInfo propertyInfo);
    }
}